package com.tian.service.impl;

import com.alibaba.fastjson.JSON;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.tian.common.CommonResult;
import com.tian.dto.ChargeUserLoginResDto;
import com.tian.dto.user.UserCouponAddReqDto;
import com.tian.entity.*;
import com.tian.enums.CouponSendTypeEnum;
import com.tian.message.MessageReqIdPrefixConstant;
import com.tian.message.UserGetCouponMessage;
import com.tian.producer.UserGetCouponProducer;
import com.tian.service.ChargeUserCouponSecondsKillService;
import com.tian.util.ChargeMarketRedisKeyPrefix;
import com.tian.util.DateUtils;
import com.tian.util.UserCacheUtil;
import org.redisson.api.RBucket;
import org.redisson.api.RScript;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;

/**
 * {@code @description:} TODO
 *
 * @author tianwc 公众号：Java后端技术全栈
 * 在线刷题 1200+java面试题和1000+篇技术文章：<a href="https://woaijava.cc/">博客地址</a>
 * {@code @date:} 2024-08-16 22:06
 * {@code @version:} 1.0
 */
@Service
public class ChargeUserCouponSecondsKillServiceImpl implements ChargeUserCouponSecondsKillService {
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private UserGetCouponProducer userGetCouponProducer;

    /**
     * 本地缓存作为活动和优惠券相关基础信息作为二级缓存
     * 原因：
     * 1：活动和优惠券相关基础信息制定好了不会再变
     * 2：减少访问redis的次数，提升系统性能
     */
    private static final Cache<Integer, ChargeCoupon> LOCAL_COUPON_CACHE = CacheBuilder.newBuilder().build();
    private static final Cache<Integer, ChargeActivity> LOCAL_ACTIVITY_CACHE = CacheBuilder.newBuilder().build();
    private static final Cache<Integer, ChargeActivityCondition> LOCAL_ACTIVITY_CONDITION_CACHE = CacheBuilder.newBuilder().build();

    @Override
    public CommonResult<Boolean> secondsKill(UserCouponAddReqDto userCouponAddReqDto) {

        ChargeUserLoginResDto user = UserCacheUtil.getUser();


        //3：校验用户是否已经抢购过,或者次数超限
        ChargeCoupon chargeCoupon = LOCAL_COUPON_CACHE.asMap().get(userCouponAddReqDto.getCouponId());
        if (chargeCoupon == null) {
            RBucket<Object> couponBucket = redissonClient.getBucket(ChargeMarketRedisKeyPrefix.CHARGE_COUPON_KEY + userCouponAddReqDto.getCouponId());
            chargeCoupon = JSON.parseObject(couponBucket.get().toString(), ChargeCoupon.class);
        }
        //活动发放类型
        if(chargeCoupon.getSendType()== CouponSendTypeEnum.ACTIVITY.getSendType()){
            // TODO: 2024-10-21 活动关联优惠券关系待完善
            //1：校验活动是否开始
            ChargeActivity chargeActivity = LOCAL_ACTIVITY_CACHE.asMap().get(userCouponAddReqDto.getCouponId());
            if (chargeActivity == null) {
                RBucket<String> chargeActivityBucket = redissonClient.getBucket(ChargeMarketRedisKeyPrefix.ACTIVITY_CONDITION_INFO_KEY + userCouponAddReqDto.getCouponId());
                chargeActivity = JSON.parseObject(chargeActivityBucket.get(), ChargeActivity.class);
                LOCAL_ACTIVITY_CACHE.put(userCouponAddReqDto.getCouponId(), chargeActivity);
            }

            ChargeActivityCondition chargeActivityCondition = LOCAL_ACTIVITY_CONDITION_CACHE.asMap().get(userCouponAddReqDto.getCouponId());
            if (chargeActivityCondition == null) {
                RBucket<String> chargeActivityConditionBucket = redissonClient.getBucket(ChargeMarketRedisKeyPrefix.ACTIVITY_CONDITION_KEY + chargeActivity.getActivityConditionId());
                chargeActivityCondition = JSON.parseObject(chargeActivityConditionBucket.get(), ChargeActivityCondition.class);
            }

            Date startTime = chargeActivityCondition.getStartTime();
            if (startTime.after(new Date())) {
                return CommonResult.failed("活动未开始");
            }
            //2：校验活动是否结束
            Date endTime = chargeActivityCondition.getEndTime();
            if (endTime.before(new Date())) {
                return CommonResult.failed("活动已结束");
            }
        }


        RBucket<Integer> secondKillBucket = redissonClient.getBucket(ChargeMarketRedisKeyPrefix.USER_SECONDS_KILL_COUPON_KEY + user.getId() + ":" + userCouponAddReqDto.getCouponId());
        Integer limit = chargeCoupon.getLimit();
        //抢购次数
        if(limit==1){
            //判断是否已经抢购过次数是否超限
            if (secondKillBucket.get() != null && chargeCoupon.getLimitTimes() <= secondKillBucket.get()) {
                return CommonResult.failed("您已经抢购过");
            }
        }


        //4：校验用户积分是否足够
        RBucket<ChargeUserPoint> userPointBucket = redissonClient.getBucket(ChargeMarketRedisKeyPrefix.USER_POINT_KEY + userCouponAddReqDto.getCouponId());
        ChargeUserPoint chargeUserPoint = JSON.parseObject(userPointBucket.get().toString(), ChargeUserPoint.class);
        //判断优惠券是否为积分兑换类型
        if(chargeCoupon.getSendType()== CouponSendTypeEnum.POINT.getSendType()){
            Integer needPoint = chargeCoupon.getPoint();
            if (chargeUserPoint.getPoint() < needPoint) {
                return CommonResult.failed("积分不足");
            }
        }


        //5：抢购库存
        String luaScript = "if redis.call('lpop', KEYS[1]) then return 1 else return 0 end";
        List<Object> keys = new ArrayList<>();
        keys.add(ChargeMarketRedisKeyPrefix.CHARGE_COUPON_STOCK_QUANTITY_KEY + userCouponAddReqDto.getCouponId());
        Long result = redissonClient.getScript()
                .eval(RScript.Mode.READ_WRITE, luaScript, RScript.ReturnType.INTEGER, keys);
        //抢购成功
        if (result == 1) {
            //7：丢到消息队列，消费者取真正给用户添加优惠券，同时用户积分释放，减数据库库存。
            UserGetCouponMessage userGetCouponMessage = new UserGetCouponMessage();
            userGetCouponMessage.setCouponId(userCouponAddReqDto.getCouponId());
            userGetCouponMessage.setUserId(user.getId());
            userGetCouponMessage.setReqId(MessageReqIdPrefixConstant.USER_GET_COUPON_REQ_ID_PREFIX
                    + UUID.randomUUID() + DateUtils.formatDefaultDateMs());
            userGetCouponProducer.sendUserGetCoupon(userGetCouponMessage);
            return CommonResult.success();
        }
        return CommonResult.success();
    }
}
